#include "udp.h"

static void* recvPackageThread(void *arg);

UDP::UDP(const int port)
{
	listenPort = port;
	listenSock = -1;
	isRunning = true;

	init(port);
}

UDP::~UDP()
{
	if (listenSock > 0)
	{
		close(listenSock);
		listenSock = -1;
	}
}

int UDP::init(const int port)
{
	listenSock = startListen(port);
	if (listenSock < 0)
	{
		std::cout << "startListen fail ! ret = " << listenSock << std::endl;
		return -1;
	}

	pthread_t pid;
	pthread_create(&pid, NULL, recvPackageThread, (void *)this);
	return 0;
}

static void* recvPackageThread(void *arg)
{
	UDP *udp = (UDP *)arg;
	udp->recvPackage();
}

int UDP::startListen(const int port)
{
	int iSocket;
	int flag = 1;
	int len = sizeof(int);
	socklen_t svrlen;
	struct sockaddr_in localaddr;

	if ((iSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
	{
		return -1;
	}

	flag = fcntl(iSocket, F_GETFL, 0);
	flag |= O_NONBLOCK;
	if (fcntl(iSocket, F_SETFL, flag) == -1)
	{
		close(iSocket);
		return -2;
	}

	svrlen = sizeof(localaddr);
	memset((char *)&localaddr, 0, svrlen);

	localaddr.sin_family = AF_INET;
	localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	localaddr.sin_port = htons(port);

	if (bind(iSocket, (struct sockaddr *)&localaddr, svrlen) == -1)
	{
		close(iSocket);
		return -3;
	}

	std::cout << "listening udp data at port " << port << std::endl;
	return iSocket;
}

void UDP::recvPackage()
{
	int ret;
	socklen_t svrlen = sizeof(struct sockaddr_in);
	Epollop epInfo;
	epInfo.epfd = epoll_create(EPOLL_MAX_EVENT);
	if (epInfo.epfd <= 0)
	{
		return;
	}

	struct epoll_event *ev = &(epInfo.events[EPOLL_MAX_EVENT]);
	ev->events = EPOLLIN | EPOLLET;
	ev->data.fd = listenSock;
	ret = epoll_ctl(epInfo.epfd, EPOLL_CTL_ADD, listenSock, ev);
	if (ret < 0)
	{
		return;
	}

	unsigned char udpRecvPackageData[MAX_UDP_PACKAGE_SIZE];
	int packageDataSize;
	int nfds, epfd = epInfo.epfd;
	struct epoll_event *allEpev = epInfo.events;
	struct epoll_event *epev = &allEpev[0];
	while (isRunning)
	{
		nfds = epoll_wait(epfd, allEpev, EPOLL_MAX_EVENT, -1);
		if (nfds < 0)
		{
			std::cout << "nfds < 0 = nfds = " << nfds << std::endl;
			continue;
		}

		for (int i = 0; i < nfds; i++)
		{
			epev = &(allEpev[i]);
			if (epev->events & EPOLLIN)
			{
				packageDataSize = recvfrom(epev->data.fd, udpRecvPackageData, MAX_UDP_PACKAGE_SIZE, 0, (struct sockaddr*)&remoteNetAddr, &svrlen);
				if (packageDataSize < 1)
				{
					break;
				}

				dealPackage(udpRecvPackageData, packageDataSize, &remoteNetAddr);
			}
		}
	}
}
